/**
 * 
 */
package com.myworld.distributedLock;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

/**
 * @ClassName: RedisDistributedLockHandlerImpl
 * @Description:分布式锁工具-redis实现
 * @author zhuqiang
 * @date 2018年12月21日 下午2:45:37
 */
@Service("distributedLockHandler")
public class RedisDistributedLockHandlerImpl implements DistributedLockHandler {

	private static final Logger logger = LoggerFactory.getLogger(RedisDistributedLockHandlerImpl.class);
	/**
	 * 1s的时长,ms
	 */
	private static final int ONE_SECOND = 1000;
	/**
	 * 默认的获取锁操作超时时长，单位ms,默认10秒
	 */
	public static final int DEFAULT_ACQUIRE_TIMEOUT_MILLIS = Integer.getInteger("com.hori.jedis.lock.acquiry.millis",
			10 * ONE_SECOND);
	/**
	 * 默认的while循环内部尝试获取锁的时间间隔，单位ms,默认100ms
	 */
	public static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = Integer
			.getInteger("com.hori.jedis.lock.acquiry.resolution.millis", 100);
	/**
	 * 依赖redisTemplate
	 */
	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * 操作redis获取全局锁
	 * 
	 * @param lock
	 *            锁的名称
	 * @param timeout
	 *            获取的超时时间,单位ms
	 * @param tryInterval
	 *            多少ms尝试一次
	 * @param lockExpireTime
	 *            获取成功后锁的过期时间,单位ms
	 * @return true 获取成功，false获取失败
	 */
	@SuppressWarnings("unchecked")
	private boolean getLock(DistributedLock newLock, long timeout, long tryInterval) {
		// logger.info(Thread.currentThread().getName() +
		// "竞争锁:"+newLock.getLockKey()+"，参数，
		// timeout:"+timeout+",tryInterval:"+tryInterval);
		long start = System.currentTimeMillis();
		try {
			while (timeout >= 0) {
				// logger.info(Thread.currentThread().getName() + "
				// 尝试获取分布式锁锁:"+newLock);
				// 尝试获取锁
				if (redisTemplate.opsForValue().setIfAbsent(newLock.getLockKey(), newLock.toString())) {// redis
																										// setNx
																										// 成功，则获得锁，返回true
					newLock.setLoginSuccess();// 设置锁对象状态为已经竞争到锁的状态
					// logger.info(Thread.currentThread().getName() + " : get
					// lock
					// successed:"+newLock+",获取锁用时："+(System.currentTimeMillis()-start)+"!!!");
					return true;
				}
				// 无法setNx成功，则判断已经存在的锁是否已经超时
				final String currentValueStr = (String) redisTemplate.opsForValue().get(newLock.getLockKey());
				// logger.info("无法setNx成功，则判断已经存在的锁是否已经超,currentValueStr:"+currentValueStr);
				if (StringUtils.isBlank(currentValueStr)) {// 获取到上一个锁数据为空，说明上一个锁刚好释放，则立即再参与竞争锁
					timeout -= tryInterval;
					continue;
				}
				final DistributedLock currentLock = DistributedLock.fromString(currentValueStr);// 已经存在的锁
				if (currentLock.isExpiredAndMine(currentLock.getUUID())) {// 已经存在的锁，已经超时
					String oldValueStr = (String) redisTemplate.opsForValue().getAndSet(newLock.getLockKey(),
							newLock.toString());// 使用getAndSet命令竞争锁
					if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {// 成功竞争到锁
						newLock.setLoginSuccess();// 设置锁对象状态为已经竞争到锁的状态
						// logger.info(Thread.currentThread().getName() + " :
						// get lock
						// successed:"+newLock+",获取锁用时："+(System.currentTimeMillis()-start)+"!!!,上个锁超时才能获取到");
						return true;
					}
				}
				timeout -= tryInterval;
				Thread.sleep(tryInterval);// 休眠+下次循环

			}
			// 竞争锁超时，返回获取不到锁
			logger.info(Thread.currentThread().getName() + " : get lock ||timeout||:" + newLock);
			return false;
		} catch (Exception e) { // #####如果redis操作发生异常，则会快速失败
			e.printStackTrace();
			logger.error("竞争锁，发生异常：" + e.getMessage());
			logger.info(Thread.currentThread().getName() + " : get lock ||快速失败||:" + newLock);
			return false;
		}
	}

	/**
	 * 尝试获取全局锁
	 * 
	 * @param lock
	 *            锁的名称
	 * @return true 获取成功，false获取失败
	 */
	@Override
	public boolean tryLock(DistributedLock lock) {
		return getLock(lock, DEFAULT_ACQUIRE_TIMEOUT_MILLIS, DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
	}

	/**
	 * 尝试获取全局锁
	 * 
	 * @param lock
	 *            锁的名称
	 * @param timeout
	 *            尝试加锁操作的最长时间 单位ms
	 * @return true 获取成功，false获取失败
	 */
	@Override
	public boolean tryLock(DistributedLock lock, long timeout) {
		return getLock(lock, timeout, DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
	}

	/**
	 * 尝试获取全局锁
	 * 
	 * @param lock
	 *            锁的名称
	 * @param timeout
	 *            尝试加锁操作的最长时间 单位ms
	 * @param tryInterval
	 *            多少毫秒尝试获取一次
	 * @return true 获取成功，false获取失败
	 */
	public boolean tryLock(DistributedLock lock, long timeout, long tryInterval) {
		return getLock(lock, timeout, tryInterval);
	}

	/**
	 * 尝试获取全局锁
	 * 
	 * @param lock
	 *            锁的名称
	 * @param timeout
	 *            尝试加锁操作的最长时间 单位ms
	 * @param tryInterval
	 *            多少毫秒尝试获取一次
	 * @param lockExpireTime
	 *            锁的过期
	 * @return true 获取成功，false获取失败
	 */
	@Override
	public boolean tryLock(DistributedLock lock, long timeout, long tryInterval, long lockExpireTime) {
		return getLock(lock, timeout, tryInterval);
	}

	/**
	 * 释放锁
	 */
	@Override
	public void releaseLock(DistributedLock lock) {
		if (lock.getLockSuccess()) {
			this.redisTemplate.delete(lock.getLockKey());
			// logger.info(Thread.currentThread().getName() + " : del
			// lock:"+lock.getLockKey()+" successed");
		} else {
			// logger.info(Thread.currentThread().getName() + " : del
			// lock:"+lock.getLockKey()+" failed,the log is not lockSuccess");
		}
	}
}
